
#include <BLabel>
#include <BButton>
#include <BVBoxLayout>
#include <BHBoxLayout>
#include <BSplitter>
#include <BMessageBox>

#include "FreshDialog.h"

FreshDialog::FreshDialog(BWidget* parent) : BDialog(parent)
{
	BHBoxLayout* hlayout = new BHBoxLayout();
	{
		_treeWidget = new BTreeWidget();
		_treeWidget->setMinSize(300, 300);

		_spacer = new BSpacer(56, 0, Policy_Fixed);

		hlayout->addWidget(_treeWidget);
		hlayout->addSpacer(_spacer);
	}

	BHBoxLayout* buttonLayout = new BHBoxLayout();
	{
		_saveButton = new BButton("save");
		_cancelButton = new BButton("cancel");

		buttonLayout->addSpring(10);
		buttonLayout->addWidget(_saveButton);
		buttonLayout->addWidget(_cancelButton);
	}
	this->setLayout(buttonLayout, Part_Bottom);

	connect(_saveButton, Signal_Clicked, &FreshDialog::procSave);
	connect(_cancelButton, Signal_Clicked, &FreshDialog::procCancel);
}
FreshDialog::~FreshDialog()
{

}

bool FreshDialog::open(const BString& fileName)
{
	_fileName = fileName;

	BFile file(fileName);
	if (!file.open(BFile::Mode_Read))
		return false;

	_headRoot = new BXmlNode("html");
	_headRoot->setFormat("<>\n</");
	BXmlNode* headNode = _headRoot->addChild("head");
	headNode->setFormat("<>\n</>\n");
	{
		BXmlNode* metaNode = headNode->addChild("meta");
		metaNode->setAttrib("charset", "utf-8");
		metaNode->setFormat("</>\n");

		BXmlNode* titleNode = headNode->addChild("title");
		titleNode->setText(fileName.name());
		titleNode->setFormat("</>\n");

		BXmlNode* linkNode = headNode->addChild("link");
		linkNode->setAttrib("rel", "stylesheet");
		linkNode->setAttrib("type", "text/css");
		linkNode->setAttrib("href", "../style.css");
	}

	BXmlNode* body = _headRoot->addChild("body");
	body->setFormat("<>\n</>\n");
	BString text;
	file.readString(text);

	int pos = 0;
	BString line;
	BStringList words;

	while (pos < text.size())
	{
		pos = text.line(line, pos);
		if (false == line.contain("class") || false == line.contain(" : "))
			continue;
		line.remove("class");
		line.remove("BWEAPI");
		line.simplify();
		if (line.empty())
			continue;
		if (0 == line.words(words))
			continue;

		BString class_name = words[0];

		int inherits_pos = text.find(" : public ");
		BString class_inherits = text.word(inherits_pos + 10);

		BXmlNode* class_name_node = new BXmlNode("h1");
		class_name_node->setText(class_name);
		class_name_node->setFormat("/>\n");
		body->addChild(class_name_node);

		BXmlNode* inherits_node = new BXmlNode("p");
		inherits_node->addText("Inherites from ");
		inherits_node->setFormat("/>\n");

		BXmlNode* linkNode = inherits_node->addChild("a");
		linkNode->setText(class_inherits);
		class_inherits.append(".html");
		linkNode->setAttrib("href", class_inherits);

		body->addChild(inherits_node);

		break;
	}

	int number = 0;

	BXmlNode* public_node = new BXmlNode("h2");
	public_node->setText("public");
	public_node->setFormat("/>\n");
	body->addChild(public_node);

	pos = text.find("public:");
	pos = text.line(line, pos);
	collectMethods(body, text, pos, number);

	BXmlNode* protected_node = new BXmlNode("h2");
	protected_node->setText("protected");
	protected_node->setFormat("/>\n");
	body->addChild(protected_node);

	pos = text.find("protected:");
	pos = text.line(line, pos);
	collectMethods(body, text, pos, number);

	createDescription(body);

	for (int i = 0; i < body->childCount(); i++)
	{
		BXmlNode* child = body->child(i);

		if (child->text() == "public")
		{
			BXmlNode* document_public = body->addChild("h2");
			document_public->setAttrib("id", "docs");
			document_public->setAttrib("tag", "public");
			document_public->setText("Public Method Document : ");
			document_public->setFormat("</>\n");
			continue;
		}

		if (child->text() == "protected")
		{
			BXmlNode* document_protected = body->addChild("h2");
			document_protected->setAttrib("id", "docs");
			document_protected->setAttrib("tag", "protected");
			document_protected->setText("Protected Method Document : ");
			document_protected->setFormat("</>\n");
			continue;
		}

		if (child->name() == "method" && child->hasAttrib("num"))
		{
			BXmlNode* method = body->addChild("method");
			method->setAttrib("id", child->attrib("num"));
			method->setFormat(">\n</>\n");

			BXmlNode* method_title = method->addChild("div");
			method_title->setText(child->plainText());
			method_title->setFormat("\n<</>\n");

			BXmlNode* method_doc = method->addChild("p");
			method_doc->setAttrib("tag", "doc");
			method_doc->setText("this is method comment");
			method_doc->setFormat("/>\n");

			method->addText("See also : ");

			body->addChild("br")->setFormat("/>\n");
		}
	}

	BTreeItem* descItem = _treeWidget->addTopItem("Description");
	BXmlNode* descNode = body->child("desc");
	descItem->setValue(descNode);

	BTreeItem* publicItem = _treeWidget->addTopItem("public");
	attachPublicMethods(publicItem);

	BTreeItem* protectedItem = _treeWidget->addTopItem("protected");
	attachProtectedMethods(protectedItem);

	return true;
}
bool FreshDialog::load(const BString& htmlFileName)
{
	_htmlFileName = htmlFileName;
	BFile file(_htmlFileName);
	if (!file.open(BFile::Mode_Read))
		return false;

	BXmlDocument document;
	_htmlRoot = document.read(file);
	if (!_htmlRoot)
		return false;
	
	BTreeItem* descItem = _treeWidget->topItem("Description");
	BXmlNode* descNode = descItem->value();
	BXmlNode* htmlDescTextNode = _htmlRoot->locate("body/desc/p");
	if (descItem && htmlDescTextNode)
	{
		BXmlNode* descTextNode = descNode->child("p");
		const BString& descText = htmlDescTextNode->plainText();
		descTextNode->setText(descText);
	}

	BTreeItem* publicItem = _treeWidget->topItem("public");
	for (int i = 0; i < publicItem->childCount(); i++)
	{
		BTreeItem* methodItem = publicItem->child(i);
		BXmlNode* methodNode = methodItem->value();
		BString desc("body/method:id=");
		desc += methodNode->attrib("id");
		BXmlNode* node = _htmlRoot->locate(desc);
		if (node)
		{
			BXmlNode* existDocNode = node->child("p:tag=doc");
			BXmlNode* docNode = methodNode->child("p:tag=doc");
			BXmlNode* methodNode = methodItem->value();
			if (docNode && existDocNode)
			{
				docNode->setText(existDocNode->plainText());
			}

			BXmlNode* existLinkNode = node->child("p : tag = see-also;");

		}
	}

	return true;
}
bool FreshDialog::save()
{
	BFile file(_htmlFileName);
	if (!file.open(BFile::Mode_Write))
		return false;

	BXmlDocument document;
	bool ret = document.write(file, _headRoot);
	if (!ret)
		return false;

	if (this->title().contain('*'))
	{
		BString title = this->title();
		title.remove('*');
		this->setTitle(title);
	}
	return true;
}

int FreshDialog::strideMethodContent(const BString& text, int pos)
{
	int depth = 0;
	int level = 0;
	while (pos < text.size())
	{
		if (text[pos] == '{')
		{
			level++;
			depth++;
		}
		if (text[pos] == '}')
			level--;
		if (text[pos] == '\n' && depth > 0 && level == 0)
			return pos + 1;
		pos++;
	}
	return pos;
}

void FreshDialog::createDescription(BXmlNode* body)
{
	BXmlNode* descNode = body->addChild("desc");
	descNode->setFormat("<>\n</>\n");

	BXmlNode* desc_title = descNode->addChild("h2");
	desc_title->setAttrib("id", "desc");
	desc_title->setText("Description : ");
	desc_title->setFormat("/>\n");

	BXmlNode* desc_doc = descNode->addChild("p");
	desc_doc->setAttrib("tag", "doc");
	desc_doc->setText("this is method comment");
	desc_doc->setFormat("/>\n");

	descNode->addText("See also : ");

	body->addChild("br")->setFormat("/>\n");
}
void FreshDialog::collectMethods(BXmlNode* body, const BString& text, int& pos, int& number)
{
	BString line;
	while (pos < text.size())
	{
		pos = text.line(line, pos);
		if (line.contain("protected:"))
			break;
		if (line.contain("private:"))
			break;
		if (line.contain("};"))
			break;
		if (line.contain("public:"))
			continue;
		if (line.contain("template"))
			continue;
		line.simplify();
		if (line.endsWith(')') || line.endsWith("const"))
		{
			pos = strideMethodContent(text, pos);
			line.append(';');
		}
		if (line.empty())
			continue;

		BString numberText(number++);

		BXmlNode* method_node = body->addChild("method");
		method_node->setAttrib("num", numberText);

		int name_end = line.find("(");
		int name_begin = line.find("operator");
		if (name_begin == line.size())
			name_begin = name_end - 1;
		while (name_begin > 0)
		{
			if (isspace(line[name_begin]))
			{
				name_begin++;
				break;
			}
			name_begin--;
		}

		method_node->addText(line.substr(0, name_begin));
		{
			numberText.prepend('#');
			BXmlNode* method_link = method_node->addChild("a");
			method_link->setText(line.substr(name_begin, name_end));
			method_link->setAttrib("href", numberText);
		}
		method_node->addText(line.substr(name_end));

		body->addChild("br")->setFormat("/>\n");
	}
}

void FreshDialog::attachPublicMethods(BTreeItem* item)
{
	BXmlNode* node = _headRoot->locate("/body/h2:id=docs:tag=public");
	if (!node)
		return;
	node = node->next();
	BString text;
	while (node)
	{
		if (node->name() == "h2")
			break;
		if (node->name() == "method" && node->hasAttrib("id"))
		{
			text.reset();
			text << '(' << node->attrib("id") << ')' << node->child("div")->plainText();
			BTreeItem* child = item->addChild(text);
			child->setValue(node);
		}
		node = node->next();
	}
}
void FreshDialog::attachProtectedMethods(BTreeItem* item)
{
	BXmlNode* node = _headRoot->locate("/body/h2:id=docs:tag=protected");
	if (!node)
		return;
	node = node->next();
	while (node)
	{
		if (node->name() == "method" && node->hasAttrib("id"))
		{
			const BString& title = node->child("div")->plainText();
			BTreeItem* child = item->addChild(title);
			child->setValue(node);
		}
		node = node->next();
	}
}

void FreshDialog::procSave()
{
	if (this->save())
	{
		this->done(1);
		return;
	}
	this->done(0);
	BMessageBox::warning(this, "Warning", "Save html file failed.");
}
void FreshDialog::procCancel()
{
	this->done(0);
}

void FreshDialog::updateEvent(const BEvent& event)
{

}
